// Effective C++ 条款36：绝不重新定义继承而来的非虚函数

// 核心原则
// 条款36的核心是："绝不重新定义继承而来的非虚函数"（Never redefine an inherited non-virtual function）。

// 详细解释
// 为什么不应该重新定义继承而来的非虚函数？
// 当一个成员函数被声明为非虚函数时，这表明该函数在整个继承层次中应该表现出一致的行为。非虚函数的调用是静态绑定的，这意味着调用哪个函数版本取决于对象的静态类型（声明类型），而不是对象的动态类型（实际类型）。

// 问题示例
class Base
{
public:
    void mf() { std::cout << "Base::mf()" << std::endl; }
};

class Derived : public Base
{
public:
    void mf() { std::cout << "Derived::mf()" << std::endl; } // 重新定义了非虚函数
};

int main()
{
    Derived d;
    Base *pb = &d;
    Derived *pd = &d;

    pb->mf(); // 调用 Base::mf()
    pd->mf(); // 调用 Derived::mf()
}
// 这段代码会导致令人困惑的行为：同一个对象d，当通过不同类型的指针访问时，会调用不同的函数版本。
// 违反了面向对象设计的基本原则
// 1.破坏了IS-A关系：如果Derived是一个Base，那么Derived应该表现得像一个Base，包括其所有非虚函数的行为。
// 2.违反了里氏替换原则：派生类对象应该能够在任何使用基类对象的地方正确工作，而不改变程序的行为。

// 正确的做法
// 如果你需要在派生类中改变函数行为，应该在基类中将该函数声明为虚函数：
class Base
{
public:
    virtual void mf() { std::cout << "Base::mf()" << std::endl; }
};

class Derived : public Base
{
public:
    virtual void mf() override { std::cout << "Derived::mf()" << std::endl; }
};
// 这样，无论通过什么类型的指针或引用访问对象，都会调用与对象实际类型相对应的函数版本。

// 实际应用
// 名称隐藏问题
// 重新定义非虚函数还会导致名称隐藏问题，特别是当函数有重载版本时：
class Base
{
public:
    void mf() {}
    void mf(int) {}
};

class Derived : public Base
{
public:
    void mf() {} // 这不仅重新定义了无参版本，还隐藏了带参数的版本
};

Derived d;
d.mf();  // 调用 Derived::mf()
d.mf(1); // 错误！Derived::mf() 隐藏了 Base::mf(int)

// 避免混淆的方法
// 使用using声明使基类的所有重载版本在派生类中可见：
class Derived : public Base
{
public:
    using Base::mf; // 使所有版本的Base::mf在Derived中可见
    void mf() {}    // 只重新定义无参版本
};
// 或者，如果函数行为确实需要变化，将其设计为虚函数。
// 总结
// 条款36强调了一个重要的C++设计原则：非虚函数代表不变的行为，虚函数代表可变的行为。如果你需要在派生类中改变函数的行为，应该在基类中将其声明为虚函数，而不是在派生类中重新定义非虚函数。

// 这条规则帮助我们创建更加一致、可预测和易于维护的类层次结构。